package com.nzw.service.impl.common;

import com.nzw.utils.ConvertUtil;
import com.nzw.utils.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.Column;
import javax.persistence.Table;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by 许湛司 on 2017/4/29.
 */
public class AbstractBaseService {
    public Logger logger = LoggerFactory.getLogger(AbstractBaseService.class);

    /**
     * 把从dao层取到的map数据封装到类实体
     *
     * @param param 字段
     * @param clazz 类
     * @param <T>
     * @return
     */
    public <T> T getBean(Map<String, Object> param, Class<T> clazz) {
        if (param == null || param.size() == 0) {
            return null;
        }
        Object value = null;
        Class[] paramTypes = new Class[1];
        Object obj = null;
        try {
            //创建实例
            obj = clazz.newInstance();
            Field[] f = clazz.getDeclaredFields();
            List<Field[]> flist = new ArrayList<Field[]>();
            flist.add(f);
            Class superClazz = clazz.getSuperclass();
            while (superClazz != null) {
                f = superClazz.getFields();
                flist.add(f);
                superClazz = superClazz.getSuperclass();
            }
            for (Field[] fields : flist) {
                for (Field field : fields) {
                    String fieldName = field.getName();
                    if (!"serialVersionUID".equals(fieldName)) {
                        paramTypes[0] = field.getType();
                        Method setMethod = null;
                        //调用相应对象的set方法
                        String methodName = "set" + StringUtil.firstUpperCase(fieldName);
                        String getMethodName = "get" + StringUtil.firstUpperCase(fieldName);
                        setMethod = clazz.getMethod(methodName, paramTypes);
                        Column column = clazz.getMethod(getMethodName).getAnnotation(Column.class);//加上这个执行10条记录要慢两秒
                        if (column != null) {
                            value = param.get(column.name());
                            if (value != null) {
                                setMethod.invoke(obj, ConvertUtil.getValue(value.toString(), fieldName, paramTypes[0]));
                            }
                        }
                    }
                }
            }
        } catch (InstantiationException e) {
            logger.error(e.getMessage(), e);
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        } catch (SecurityException e) {
            logger.error(e.getMessage(), e);
        } catch (NoSuchMethodException e) {
            logger.error(e.getMessage(), e);
        } catch (IllegalArgumentException e) {
            logger.error(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage(), e);
        }
        return (T) obj;
    }

    /**
     * 单条记录插入
     *
     * @param obj
     * @return
     */
    public Map<String, Object> reflectInsert(Object obj) {
        if (obj == null) {
            throw new NullPointerException();
        }
        Map<String, Object> resMap = new LinkedHashMap<String, Object>();
        Class<?> objClass = obj.getClass();
        Table table = objClass.getAnnotation(Table.class);
        resMap.put("table", table.name());
        Map<String, Object> columnMap = getInsertColumns(obj, objClass);
        columnMap.remove("id");
        resMap.put("column", columnMap);
        return resMap;
    }

    /**
     * 批量插入
     *
     * @param listObj 实体集合，必须同一个类名
     * @param <T>
     * @return
     */
    public <T> Map<String, Object> reflectBatchInsert(List<T> listObj) {
        if (listObj != null) {
            Map<String, Object> resMap = new LinkedHashMap<String, Object>();
            List<Map<String, Object>> listColumnMap = new ArrayList<Map<String, Object>>();
            StringBuffer columnNames = new StringBuffer();
            boolean bool = true;
            for (Object obj : listObj) {
                Class<?> objClass = obj.getClass();
                if (bool) {
                    Table table = objClass.getAnnotation(Table.class);
                    resMap.put("table", table.name());
                }
                Map<String, Object> columnMap = getBatchInsertColumns(obj, objClass, columnNames, bool);
                if (columnMap.containsKey("id"))
                    columnMap.remove("id");
                listColumnMap.add(columnMap);
                bool = false;
            }
            if (columnNames.length() > 0) {
                String columnName = columnNames.substring(0, columnNames.length() - 1);

                resMap.put("columnNames", columnName);
            }
            resMap.put("columns", listColumnMap);
            return resMap;
        }
        return null;
    }

    private Map<String, Object> getBatchInsertColumns(Object obj, Class<?> objClass, StringBuffer columnNames, boolean bool) {
        Map<String, Object> columnMap = getColumns(obj, objClass, columnNames, bool, false,false);
        return columnMap;
    }

    private Map<String, Object> getInsertColumns(Object obj, Class<?> objClass) {
        Map<String, Object> columnMap = getColumns(obj, objClass, null, false, true,false);
        return columnMap;
    }

    public Map<String, Object> queryColumnsEscapeNull(Object entity) {
        if (entity == null)
            return null;
        Map<String, Object> parmas = new HashMap<>();
        Map<String, Object> columns = new HashMap<>();
        Map<String, Object> condition = new HashMap<>();

        Class<?> clazz = entity.getClass();
        parmas.put("table", clazz.getAnnotation(Table.class).name());

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String name = field.getName();
            if (!"serialVersionUID".equals(name)) {//序列号不用取
                String getMethodName = "get" + StringUtil.firstUpperCase(name);
                try {
                    Method method = clazz.getMethod(getMethodName);
                    Column column = method.getAnnotation(Column.class);
                    if (column == null)
                        continue;
                    Object val = method.invoke(entity);
                    if (val != null) {
                        columns.put(name, val);
                    }
                    if (column.name().equals("id")) {//把主键id当成条件
                        condition.put("id", val);
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        if (columns.isEmpty() || condition.isEmpty())
            return null;
        parmas.put("column", columns);
        parmas.put("condition", condition);
        return parmas;

    }

    public static void main(String[] args) throws UnsupportedEncodingException {

    }

    /**
     * 获取字段
     * @param obj
     * @param objClass
     * @param columnNames 数据库字段名
     * @param bool        是否反回字段名
     * @param condition   是否返回ID作为条件
     * @return
     */
    private Map<String, Object> getColumns(Object obj, Class<?> objClass, StringBuffer columnNames, boolean bool,boolean isGetIdColumn ,boolean condition) {
        if (obj == null || objClass == null) {
            throw new NullPointerException();
        }
        Map<String, Object> columnMap = new LinkedHashMap<String, Object>();
        Map<String, Object> conditionMap = new LinkedHashMap<String, Object>();
        Field[] fields = objClass.getDeclaredFields();
        try {
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                field.setAccessible(true);
                String name = field.getName();
                if (!"serialVersionUID".equals(name)) {
                    String getMethodName = "get" + StringUtil.firstUpperCase(name);
                    Method method = objClass.getMethod(getMethodName);
                    Column column = method.getAnnotation(Column.class);
                    // 避免没有Column标签的参数被扫描到
                    if (column != null) {
                        if (bool) {
                            if (column.name().equals("id")&&!isGetIdColumn)
                                continue;
                            columnNames.append(column.name() + ",");
                        }
                        Object value = method.invoke(obj);
                        if ("id".equalsIgnoreCase(column.name())) {
                            if (value == null || 0 == Integer.valueOf(value.toString())) {
//    							value = UUIDGenerator.generate();
//    							objClass.getMethod(setMethodName, value.getClass()).invoke(obj, value);
                            } else {
                                conditionMap.put(column.name(), value);
                            }
                        }
                        if (condition) {
                            if (!conditionMap.isEmpty()) {
                                columnMap.put("condition", conditionMap);
                            }
                        }
                        columnMap.put(column.name(), value);
                    }
                }
            }
        } catch (IllegalArgumentException e) {
            logger.error(e.getMessage(), e);
        } catch (SecurityException e) {
            logger.error(e.getMessage(), e);
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage(), e);
        } catch (NoSuchMethodException e) {
            logger.error(e.getMessage(), e);
        }
        return columnMap;
    }


    /**
     * 反射去设置刚插入成功的实体的主键ID
     *
     * @param obj   实体
     * @param param 字段
     */
    public void reflectSetId(Object obj, Map<String, Object> param) {
        if (obj == null) {
            throw new NullPointerException();
        }
        Class<?> objClass = obj.getClass();
        Field[] fields = objClass.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            field.setAccessible(true);
            String name = field.getName();
            if (!"serialVersionUID".equals(name)) {
                String getMethodName = "get" + StringUtil.firstUpperCase(name);
                String setMethodName = "set" + StringUtil.firstUpperCase(name);
                Method method = null;
                try {
                    method = objClass.getMethod(getMethodName);
                } catch (NoSuchMethodException e) {
                    logger.error("找不到该方法:" + e.getMessage());
                } catch (SecurityException e) {
                    logger.error("无权限访问资源:" + e.getMessage());
                }
                Column column = method.getAnnotation(Column.class);
                if (column != null) {
                    if ("id".equalsIgnoreCase(column.name())) {
                        if (param.containsKey(column.name())) {
                            Object valObj = param.get(column.name());
                            Method setMethod = null;
                            try {
                                setMethod = objClass.getMethod(setMethodName, method.getReturnType());
                            } catch (NoSuchMethodException e) {
                                logger.error("找不到该方法:" + e.getMessage());
                            } catch (SecurityException e) {
                                logger.error("无权限访问资源:" + e.getMessage());
                            }

                            try {
                                setMethod.invoke(obj, valObj);
                            } catch (IllegalAccessException e) {
                                logger.error("不允许调用该方法（比如private类型的方法）:" + e.getMessage());
                            } catch (IllegalArgumentException e) {
                                logger.error("传递的参数不正确:" + e.getMessage());
                            } catch (InvocationTargetException e) {
                                logger.error("调用的方法抛出异常:" + e.getMessage());
                            }
                            break;
                        }
                    }
                }
            }
        }
    }

    /**
     * 删除单条记录
     *
     * @param obj
     * @return
     */
    public Map<String, Object> reflectDelete(Object obj) {
        if (obj == null) {
            throw new NullPointerException();
        }
        Map<String, Object> resMap = new LinkedHashMap<String, Object>();
        Class<?> objClass = obj.getClass();
        Table table = objClass.getAnnotation(Table.class);
        resMap.put("table", table.name());
        Map<String, Object> columnMap = delete(obj, objClass);
        if (columnMap == null || columnMap.isEmpty()) {
            throw new NullPointerException();
        }
        resMap.put("column", columnMap);
        return resMap;
    }

    /**
     * 根据ID删除实体
     *
     * @param objClass
     * @param objVal
     * @return
     */
    public Map<String, Object> reflectDelById(Class<?> objClass, Object objVal) {
        if (objClass == null) {
            throw new NullPointerException();
        }
        Map<String, Object> resMap = new LinkedHashMap<String, Object>();
        Map<String, Object> columnMap = new LinkedHashMap<String, Object>();
        Table table = objClass.getAnnotation(Table.class);
        resMap.put("table", table.name());
        Field[] fields = objClass.getDeclaredFields();
        try {
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                field.setAccessible(true);
                String name = field.getName();
                if (!"serialVersionUID".equals(name)) {
                    String getMethodName = "get" + StringUtil.firstUpperCase(name);
                    Method method = objClass.getMethod(getMethodName);
                    Column column = method.getAnnotation(Column.class);
                    if ("id".equalsIgnoreCase(column.name())) {
                        if (objVal == null || StringUtils.isBlank(String.valueOf(objVal))) {
                            throw new NullPointerException("主键ID不能为空");
                        }
                        columnMap.put(column.name(), objVal);
                    }
                }
            }
            resMap.put("column", columnMap);
        } catch (SecurityException e) {
            logger.error(e.getMessage(), e);
        } catch (NoSuchMethodException e) {
            logger.error(e.getMessage(), e);
        }
        return resMap;
    }

    /**
     * 批量删除
     *
     * @param listObj
     * @param <T>
     * @return
     */
    public <T> Map<String, Object> reflectBatchDelete(List<T> listObj) {
        if (listObj != null) {
            Map<String, Object> resMap = new LinkedHashMap<String, Object>();
            List<Map<String, Object>> listColumnMap = new ArrayList<Map<String, Object>>();
            for (Object obj : listObj) {
                Class<?> objClass = obj.getClass();
                Table table = objClass.getAnnotation(Table.class);
                resMap.put("table", table.name());
                Map<String, Object> columnMap = delete(obj, objClass);
                if (columnMap != null && !columnMap.isEmpty()) {
                    listColumnMap.add(columnMap);
                }
            }
            resMap.put("column", listColumnMap);
            return resMap;
        }
        return null;
    }

    /**
     * 删除实体
     *
     * @param obj
     * @param objClass
     * @return
     */
    private Map<String, Object> delete(Object obj, Class<?> objClass) {
        if (obj == null || objClass == null) {
            throw new NullPointerException();
        }
        Map<String, Object> columnMap = new LinkedHashMap<String, Object>();
        Field[] fields = objClass.getDeclaredFields();
        try {
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                field.setAccessible(true);
                String name = field.getName();
                if (!"serialVersionUID".equals(name)) {
                    String getMethodName = "get" + StringUtil.firstUpperCase(name);
                    Method method = objClass.getMethod(getMethodName);
                    Column column = method.getAnnotation(Column.class);
                    Object value = method.invoke(obj);
                    if ("id".equalsIgnoreCase(column.name())) {
                        if (value == null || StringUtils.isBlank(value.toString())) {
                            throw new NullPointerException("主键ID不能为空");
                        }
                    }
                    if (value != null)
                        columnMap.put(column.name(), value);
                }
            }
        } catch (IllegalArgumentException e) {
            logger.error(e.getMessage(), e);
        } catch (SecurityException e) {
            logger.error(e.getMessage(), e);
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage(), e);
        } catch (NoSuchMethodException e) {
            logger.error(e.getMessage(), e);
        }
        return columnMap;
    }

    private Map<String, Object> getSaveOrUpdateColumns(Object obj, Class<?> objClass, boolean condition) {
        Map<String, Object> columnMap = getColumns(obj, objClass, null, false, true,condition);
        return columnMap;
    }


    /**
     * 保存或修改
     *
     * @param obj
     * @return
     */
    public Map<String, Object> saveOrUpdate(Object obj) {
        if (obj == null) {
            throw new NullPointerException();
        }
        Map<String, Object> resMap = new LinkedHashMap<String, Object>();
        Class<?> objClass = obj.getClass();
        Table table = objClass.getAnnotation(Table.class);
        resMap.put("table", table.name());
        Map<String, Object> columnMap = getSaveOrUpdateColumns(obj, objClass, true);
        columnMap.remove("id");
        if (columnMap != null) {
            //也可以根据 Map 中是否包condition来判断是更新还是新增
            if (columnMap.containsKey("condition")) {
                Map<String, Object> conditionMap = (Map<String, Object>) columnMap.get("condition");
                if (conditionMap == null || conditionMap.isEmpty()) {
                    throw new NullPointerException("条件不能为空");
                }
                resMap.put("condition", conditionMap);
                columnMap.remove("condition");
            }
            resMap.put("column", columnMap);
        }
        return resMap;
    }

    public Map<String, Object> queryEntityByProperty(Class<?> clazz, String propertyName, Object val) {
        return queryEntity(clazz, propertyName, val, false);
    }

    /**
     * 根据属性值找到实体
     *
     * @param objClass
     * @param propertyName
     * @param value
     * @param isLoadAll    是否查询所有，此时字段值无效
     * @return
     */
    private Map<String, Object> queryEntity(Class<?> objClass, String propertyName, Object value, boolean isLoadAll) {
        if (objClass == null)
            throw new NullPointerException();
        if (!isLoadAll) {
            if (propertyName == null || StringUtils.isBlank(propertyName))
                throw new NullPointerException("属性名为空");
        }
        boolean propertyNameBool = false;//属性名是否存在
        Map<String, Object> resMap = new LinkedHashMap<String, Object>();
        Map<String, Object> conditionMap = new LinkedHashMap<String, Object>();
        StringBuffer columnNames = new StringBuffer();
        Table table = objClass.getAnnotation(Table.class);
        resMap.put("table", table.name());
        Field[] fields = objClass.getDeclaredFields();
        try {
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                field.setAccessible(true);
                String name = field.getName();
                if (!"serialVersionUID".equals(name)) {
                    String getMethodName = "get" + StringUtil.firstUpperCase(name);
                    Method method = objClass.getMethod(getMethodName);
                    Column column = method.getAnnotation(Column.class);
                    if (column != null) {
                        columnNames.append(column.name() + ",");
                        if (!isLoadAll && name.equals(propertyName)) {
                            propertyNameBool = true;
                            conditionMap.put(column.name(), value);
                        }
                    }
                }
            }
            if (!isLoadAll && !propertyNameBool) {
                throw new NullPointerException("属性名不存在");
            }
            if (columnNames.length() > 0) {
                String columnName = columnNames.substring(0, columnNames.length() - 1);
                resMap.put("columnNames", columnName);
            }
            resMap.put("condition", conditionMap);
        } catch (SecurityException e) {
            logger.error(e.getMessage(), e);
        } catch (NoSuchMethodException e) {
            logger.error(e.getMessage(), e);
        }
        return resMap;
    }

    /**
     * 查询所有实体
     *
     * @param objClass
     * @return
     */
    public Map<String, Object> reflectLoadAll(Class<?> objClass) {
        return queryEntity(objClass, null, null, true);
    }

    /**
     * 按需要的字段进行查询
     *
     * @param objectClass 查询实体
     * @param params      查询的字段
     * @param condition   查询条件
     * @return
     */
    public Map<String, Object> queryEntityEnclosureParamsAssignColumn(Class<?> objectClass, List<String> params, Map<String, Object> condition) {
        return queryEntityEnclosureParams(objectClass, params, condition, false);
    }

    /**
     * 全字段查询
     *
     * @param objectClass 查询实体
     * @param condition   查询条件
     * @return
     */
    public Map<String, Object> queryEntityEnclosureParamsWholeColumn(Class<?> objectClass, Map<String, Object> condition) {
        return queryEntityEnclosureParams(objectClass, null, condition, true);
    }

    /**
     * 封装实体便于查询数据库
     *
     * @param objectClass 实体类
     * @param params      要搜索的字段名
     * @param condition   查询条件
     * @param isWhole     是否全字段查询
     * @return
     */
    private Map<String, Object> queryEntityEnclosureParams(Class<?> objectClass, List<String> params, Map<String, Object> condition, boolean isWhole) {
        if (objectClass == null)
            throw new NullPointerException("实体不存在");
        Map<String, Object> resMap = new HashMap<>();
        Table table = objectClass.getAnnotation(Table.class);
        if (table == null)
            throw new NullPointerException("该实体没有设置table");
        resMap.put("table", table.name());
        resMap.put("condition", condition);
        StringBuffer sb = new StringBuffer();
        if (!isWhole) {//不是全属性查询
            for (String str : params) {
                sb.append(str).append(",");
            }
            String columnNames = sb.deleteCharAt(sb.length() - 1).toString();//把最后的逗号去掉
            resMap.put("columnNames", columnNames);
            return resMap;
        }
        Field[] fields = objectClass.getDeclaredFields();
        try {
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                field.setAccessible(true);
                String name = field.getName();
                if (!"serialVersionUID".equals(name)) {    // 将serialVersionUID字段剔除
                    // 通过获取getXxx()方法的注解Column来获取类包含的属性
                    String getMethodName = "get" + StringUtil.firstUpperCase(name);
                    Method method = objectClass.getMethod(getMethodName);
                    Column column = method.getAnnotation(Column.class);
                    if (column != null) {
                        sb.append(column.name() + ",");    // 将属性名连接为 xxx,xxx,xxx 的形式保存在columnNames中
                    }
                }
            }
            String columnNames = sb.deleteCharAt(sb.length() - 1).toString();//把最后的逗号去掉
            resMap.put("columnNames", columnNames);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return resMap;
    }


}
